package com.yanjiali.service.impl;

import com.alibaba.fastjson.JSON;
import com.yanjiali.constant.ESIndexConstant;
import com.yanjiali.constant.HighlightLabelConstant;
import com.yanjiali.model.R;
import com.yanjiali.model.req.ObtainTheSearchResultsByPaginationReq;
import com.yanjiali.model.req.SearchPromptReq;
import com.yanjiali.model.resp.ObtainTheSearchResultsByPaginationResp;
import com.yanjiali.model.resp.SearchPromptResp;
import com.yanjiali.service.SearchEngineService;
import com.yanjiali.vo.SearchResultVO;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Package: com.yanjiali.service.impl
 * @Author: yanjiali
 * @Created: 2025/4/17 21:31
 * TODO 两个接口测试基本功能，后续可以补充优化排序
 */
@Service
public class SearchEngineServiceImpl implements SearchEngineService {

    @Autowired
    private RestHighLevelClient client; //es客户端

    @Override
    public R<ObtainTheSearchResultsByPaginationResp> ObtainTheSearchResultsByPagination(ObtainTheSearchResultsByPaginationReq obtainTheSearchResultsByPaginationReq) {
        try {
            // 1.准备Request
            SearchRequest request = new SearchRequest(ESIndexConstant.ES_SEARCH_RESULT_INDEX);
            // 2.准备DSL
            buildBasicQuery(obtainTheSearchResultsByPaginationReq, request);

            // 2.2.分页
            int page = obtainTheSearchResultsByPaginationReq.getPageCurrent();
            int size = obtainTheSearchResultsByPaginationReq.getPageSize();
            request.source().from((page - 1) * size).size(size);

            // 3.发送请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            // 4.解析响应
            return handleResponse(response);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * TODO 解析高亮
     * @param response
     * @return
     */
    private R<ObtainTheSearchResultsByPaginationResp> handleResponse(SearchResponse response) {
        // 4.解析响应
        SearchHits searchHits = response.getHits();

        // 4.1.获取总条数
        Long total = searchHits.getTotalHits();

        // 4.2.文档数组
        SearchHit[] hits = searchHits.getHits();

        // 4.3.TODO 存储需要返回的结果
        List<SearchResultVO> hotels = new ArrayList<>();

        // 4.4.遍历
        for (SearchHit hit : hits) {
            // 获取文档source
            String json = hit.getSourceAsString();

            SearchResultVO searchResultVO = JSON.parseObject(json, SearchResultVO.class);

            // 获取高亮结果
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (!CollectionUtils.isEmpty(highlightFields)) {
                // 根据字段名获取高亮结果
                HighlightField highlightField = highlightFields.get("title");
                if (highlightField != null) {
                    // 获取高亮值
                    String name = highlightField.getFragments()[0].string();
                    searchResultVO.setTitle(name);
                }
            }

            hotels.add(searchResultVO);   // 添加数据到返回结果
        }

        return R.ok(new ObtainTheSearchResultsByPaginationResp(hotels, total));
    }


    //TODO 基本测试结束，后续需要完成搜索
    /**
     * 安装拼音分词器，索引配置和增加补全字段，对应索引类也需要补全字段
     * @param searchPromptReq
     * @return
     */
    @Override
    public R<SearchPromptResp> searchPrompt(SearchPromptReq searchPromptReq) {
        try {
            // 1.准备Request
            SearchRequest request = new SearchRequest(ESIndexConstant.ES_SEARCH_RESULT_INDEX);
            // 2.准备DSL
            request.source().suggest(new SuggestBuilder().addSuggestion(
                    "suggestions",
                    SuggestBuilders.completionSuggestion("suggestion")
                            .prefix(searchPromptReq.getSuffix()) // TODO 搜索框中输入的参数
                            .skipDuplicates(true)
                            .size(10)
            ));
            // 3.发起请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);

            // 4.解析结果
            Suggest suggest = response.getSuggest();

            // 4.1.根据补全查询名称，获取补全结果
            CompletionSuggestion suggestions = suggest.getSuggestion("suggestions");

            // 4.2.获取options
            List<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();

            // 4.3.遍历
            List<String> list = new ArrayList<>(options.size());
            for (CompletionSuggestion.Entry.Option option : options) {
                String text = option.getText().toString();
                String temp = completeCharacterHighlight(text, searchPromptReq.getSuffix());
                if(null != temp) {
                    list.add(temp);
                }else {
                    list.add(text);
                }
            }

            return R.ok(new SearchPromptResp(list));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public R removeData(String id) {
        DeleteRequest deleteRequest = new DeleteRequest(ESIndexConstant.ES_SEARCH_RESULT_INDEX, ESIndexConstant.ES_DOCUMENT_TYPE, id);
        DeleteResponse deleteResponse = null;
        try {
            deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 检查响应中的结果
        if (deleteResponse.getResult() == DocWriteResponse.Result.DELETED) {
            return R.ok("删除成功");
        }

        return R.fail("删除失败");
    }

    /**
     * 给补全字段设置高亮标签格式
     * @param text  //对应搜索的文本
     * @param prefix    //对应前缀
     */
    private String completeCharacterHighlight(String text, String prefix) {
        if(prefix.length() > text.length()) {   //前缀字段的长度超过了对应的搜索文本
            return null;
        }

        //TODO 把两个都转为小写，为了使得无论大小写都可以加标签
        String lowerCaseText = text.toLowerCase();
        String lowerCasePrefix = prefix.toLowerCase();

        StringBuffer stringBuffer = new StringBuffer(text);
        int beginIndex = lowerCaseText.indexOf(lowerCasePrefix);   //找到第一次出现的位置
        if(beginIndex == -1) {  //如果找不到，大小写需要处理
            return null;
        }
        int endIndex = beginIndex + prefix.length();
        stringBuffer.insert(endIndex + beginIndex   //TODO 先插入后面的标签
                , HighlightLabelConstant.HIGHLIGHT_LABEL_SUFFIXL);
        stringBuffer.insert(beginIndex, HighlightLabelConstant.HIGHLIGHT_LABEL_PREFIX); //TODO 后插入前面的标签
        return stringBuffer.toString();
    }

    /**
     * TODO 构建查询条件
     * @param obtainTheSearchResultsByPaginationReq
     * @param request
     */
    private void buildBasicQuery(ObtainTheSearchResultsByPaginationReq obtainTheSearchResultsByPaginationReq, SearchRequest request) {
        // 1.构建BooleanQuery
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        // 2.关键字搜索
        String key = obtainTheSearchResultsByPaginationReq.getKey();
        if (key == null || "".equals(key)) {
            boolQuery.must(QueryBuilders.matchAllQuery());
        } else {
            boolQuery.must(QueryBuilders.matchQuery("all", key));
        }

        //3、算分，给是广告的提前
        FunctionScoreQueryBuilder functionScoreQuery =
                QueryBuilders.functionScoreQuery(
                        boolQuery,  //原始查询，相关性算分
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{  //function score的数组
                                new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                        QueryBuilders.termQuery("isAD", true),  //过滤条件
                                        ScoreFunctionBuilders.weightFactorFunction(10)  //对应权重
                                )
                        });


        // 4、入source
        request.source().query(functionScoreQuery);

        //5、给对应字段关键词给以高亮<em>关键词</em>
        request.source().highlighter(new HighlightBuilder().field("title").requireFieldMatch(false));
    }
}
